require "rubygems"
require "net/ssh"
require 'fileutils'

module RoadRunnerModule
  
=begin
  RRMonitor is used for Xnix system.
=end

class RRMonitor
  def initialize(opt={:server=>"0.0.0.0", :username=>"admin", :password=>"123456"})
    raise(ArgumentError,"Logger is needed!") unless opt[:log]
    @log=opt[:log]
    
    @server=opt[:server]
    
    begin
      @sess=Net::SSH.start(opt[:server], opt[:username], :password => opt[:password],:timeout => 120)
      @log.info "#{opt[:server]} ssh connection OK."
    rescue Exception => e
      @log.error e
      @log.error opt.inspect
      raise e
    end
    
    
    @log.info("monitor initialize.".center(60,"*"))
    @log.debug("monitor => #{self.inspect}")
    
    @ts=( opt[:ts] || Time.now.to_i )
    
    @path="#{File.join File.dirname(__FILE__),'..','log'}/monitor/#{@ts}"
    @log.info "monitor local log path :#{@path}"
    
    @rpath="~/.monit/#{@ts}"
    @log.info "monitor remote log path(rpath) :#{@rpath}"
    
    @sess.exec!("rm -rf #{@rpath}")
    @sess.exec!("mkdir -p #{@rpath}")
    
    @pids={}
  end
  
=begin
  run!(cmd) =>
  the cmd will be execed at remote server(0),
  and the stdout will write in log file.
  
  cmd =>[
    'ifstat',# network io
    'iostat 3',# disk io
    'vmstat 3',# memory info
    'while((1>0));do sar -u -d 3 10; done',# disk and cpu info
    'while((1>0));do sar -u -n DEV 3 10; done'# network and cpu info
    ]
  
  "while((1>0));do #{cmd}; done" is needed for non-consist output cmd.
  
  cmd sample =>
  
    run!(["while((1>0));do /home/admin/apsara/build/debug64/bin/ku --command=getallpart --appname=blog1 --interactive=false|tail -n1; done"])
    
    when exec!(cmd)
    =>
    exec!("while((1>0));do /home/admin/apsara/build/debug64/bin/ku --command=getallpart --appname=blog1 --interactive=false|tail -n1; done  >> #{kupath} 2>/dev/null &")
=end
  
  def run!(cmd=['ifstat','iostat 3','while((1>0));do sar -u -d 3 10; done','vmstat 3','while((1>0));do sar -u -n DEV 3 10; done'])
    (@log.error "run!(cmd) cmd type require array.";exit) unless cmd.is_a?(Array)
    (@log.error "run!(cmd) require commands.";exit) if cmd.none?
    
    @cmd=cmd
    
    begin
      @log.info("RUN!".center(60,"*"))
      @log.debug @server
      cmd.each { |e|  self.exec!(e)}
    rescue Exception => e
      @log.error("#{self.inspect} run error!")
      @log.error(e.to_s)
      # raise e
    end
    
  end

  def exec!(cmd)
    # >> cmd='while((1>0));do sar -u -d 3 10; # done';cmdpath="#{cmd.gsub(/([^a-zA-Z0-9\-])/,'_')}.log"
    # => "while__1_0___do_sar_-u_-d_3_10__done.log"
    
    # cmdpath="#{@rpath}/#{cmd.gsub(/[\s|\t|\$|\`|\(|\)|\&|\>|\<|\;|\||\'|\"]/,'_')}.log"
    cmdpath="#{@rpath}/#{cmd.gsub(/[^a-zA-Z0-9\-]/,'_')}.log"
    @cmdpath=cmdpath
    
    @log.debug "#{cmd} path => #{cmdpath}"
    @log.info "Start #{cmd}.".center(60,"*")
    
    @log.debug("#{cmd} >> #{cmdpath} 2>/dev/null &")
    @sess.exec!("#{cmd} >> #{cmdpath} 2>/dev/null &")
    
    @pids[cmd]=@sess.exec!("echo $!")
    @log.debug("#{cmd} pid => #{@pids[:ku]}")
  end
  
  def stop!
    @pids.each  do |key,value|
      @sess.exec!("kill -9 #{value}")
      @log.info("#{key} => #{value} stop!")
    end
    
    # if other user exec the same cmd,
    # below cmd will kill all of the cmd.
    
    # @sess.exec!(%q@ps axf|grep while|grep -v grep|awk '{printf "%s\n",$1}'|xargs kill -9@)
    # 
    # @cmd.each { |e| 
    #   @sess.exec!(%q@ps axf|grep "#{e}"|grep -v grep|awk '{printf "%s\n",$1}'|xargs kill -9@)
    # }
    
    @log.debug @server
    @log.info("Monitor STOP!".center(60,"*"))
  end
  
  def collect
    @log.debug "mkdir -p #{@path}/#{@server}"
    %x{mkdir -p #{@path}/#{@server}}
    
    path = @path.gsub('~',`echo ~`.gsub(/[\r\n]/,''))
    @log.info "Collecting...".center(60,"*")
    @log.debug "collect path => #{path}/#{@server}"
    `scp -r admin@#{@server}:#{@rpath} #{path}/#{@server}`
    @log.info "collected files => #{Dir[path+'/'+@server+'/**/*'].join($/)}"
  end
  
  #-----------------------------monit method--------------------------------#

  def self.monit(servers,log,cmd=['ifstat','iostat 3','while((1>0));do sar -u -d 3 10; done','vmstat 3','while((1>0));do sar -u -n DEV 3 10; done'],ts="PerformanceTest")

    case servers
    when Hash
      # do nothing
    when String
      File.open( servers ) { |yf| 
        begin
          servers=YAML::load( yf ) 
        rescue Exception => e
          log.error("Servers' config YAML Load Error!Plz check your yaml file => #{servers}.#{$/}#{e.to_s}")
          exit 1
        end
      }
    end
    
    _servers = {}

    servers.each do |ip,obj|
      _servers[ip] = RoadRunnerModule::RRMonitor.new({:server=>ip, :log=>log, :username=>obj[:username], :password=>obj[:password], :ts=>obj[:obj]||ts})

      _servers[ip].run! cmd
    end

    yield

    _servers.each do |k,v|
      v.stop!
      v.collect
    end

  end
  
end

end


